package dp;

/**
 * @BelongsProject: LeetCode
 * @BelongsPackage: com.elvis.leetcode.dp
 * @Author: Elvis
 * @CreateTime: 2019-05-25 09:02
 * Description:
 * 给定一个无序的整数数组，找到其中最长上升子序列的长度。
 *
 * 示例:
 *
 * 输入: [10,9,2,5,3,7,101,18]
 * 输出: 4
 * 解释: 最长的上升子序列是 [2,3,7,101]，它的长度是 4。
 * 说明:
 *
 * 可能会有多种最长上升子序列的组合，你只需要输出对应的长度即可。
 * 你算法的时间复杂度应该为 O(n2) 。
 * 进阶: 你能将算法的时间复杂度降低到 O(n log n) 吗?
 */
public class Leetcode300 {


    public int lengthOfLIS2(int[] nums) {
        int n=nums.length;
        if(n<=1) return n;
        int[] dp=new int[n];
        dp[0]=1;
        int max=1;
        for(int i=1;i<n;i++){
            dp[i]=1;
            for(int j=0;j<i;j++){
                if(nums[i]>nums[j]) dp[i]=Math.max(dp[i],dp[j]+1);
            }
            max=Math.max(dp[i],max);
        }
        return max;
    }

    public static int lengthOfLIS(int[] nums) {
        if(nums.length <= 1){
            return nums.length;
        }
        //最大长度
        int max = 1;
        //dp[i]表示第i长的子序列，最后的元素
        int[] dp = new int[nums.length + 1];
        dp[1] = nums[0];
        for(int i = 1;i < nums.length;i++){
            //如果当前元素比最大的那个子串的最后一个元素还要大
            //那就直接长度加一，新子串的最后一个元素为当前元素
            if(nums[i] > dp[max]){
                dp[++max] = nums[i];
            }else if(nums[i] < dp[max]){
                //如果当前元素比最大的那个子串的最后一个元素要小
                //那就要更新dp数组，保证每一个子串都是最优解
                int l=1,r=max;
                while(l<r){
                    int mid=(l+r)/2;
                    if(dp[mid]>=nums[i]){
                        r=mid;
                    }else l=mid+1;
                }
                dp[l]=nums[i];
            }
        }
        return max;
    }



    public static void main(String[] args) {
        int[] nums = {10,9,2,5,3,7,101,18}; //
        Leetcode300.lengthOfLIS(nums);
    }

}
